﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Collections;
using System.Net.Mail;
using AMIUtilities;
using System.Timers;
using System.Diagnostics;

//    Copyright (C) 2011 Officebroker.com LTD.
//    Neil Cadman, Alex Welding, Andrew Coombs, Jan Rygielski
//    Sir Robert Peel Mill, Mill Lane, Staffordshire, Fazely, B78 3QD, England
//
//    This file is part of AMIProxy Project.
//
//    AMIProxy Project is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    AMIProxy Project is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License (GNU AGPL)
//    along with AMIProxy Project.  If not, see <http://www.gnu.org/licenses/>.

namespace AMIProxyLogic 
{
    public class AMIProxy
    {
        // Private Variables
        private TcpListener tcpServer = null;
        private NetworkStream netStream;
        private String _username = "";
        private String _password = "";
        private String _sendFromAddress = "";
        private String _sendToAddress = "";
        private String _smtpServer = "";
        private String _logPath = "";
        private Boolean _logging = false;
        private String _debugPath;
        private DateTime _todaysDate = DateTime.MinValue;
 
        private TcpClient tcpClient;
        private List<Phone> _phones = new List<Phone>();
        private List<Channel> _channels = new List<Channel>();
        private List<ManagedSocket> _clients = new List<ManagedSocket>();
        private List<ManagedSocket> _AMIProxyListeners = new List<ManagedSocket>();
        private List<ManagedSocket> _callStatListeners = new List<ManagedSocket>();
        private StreamWriter _writer;
        private String command = "";

        // Private Delegates
        private delegate void readStreamDelegate(NetworkStream stream);
        private delegate void listenForConnectionsDelegate();
        private delegate void readSocketDelegate(Socket socket);

        // Public Enums
        public enum State
        {
            Free,
            Recieving,
            Busy
        }

        // Public Constructors
        public AMIProxy()
        {
            InitaliseServer(5038);
        }

        // Public Methods
        public void InitiateConnection(String username, String password, String hostName, Int32 port, String sendFromAddress, String sendToAddress, String smtpServer, String logPath, Boolean logging)
        {

            _username = username;
            _password = password;
            _sendFromAddress = sendFromAddress;
            _sendToAddress = sendToAddress;
            _smtpServer = smtpServer;
            _logPath = logPath;
            _logging = logging;

            tcpClient = new TcpClient(hostName, port);
            netStream = tcpClient.GetStream();

            readStreamDelegate readSteam = new readStreamDelegate(readStream);
            readSteam.BeginInvoke(netStream, new AsyncCallback(readStreamEnd), null);
        }
        public void ShutdownServer()
        {
            netStream.Close();
            tcpClient.Close();

            foreach (ManagedSocket managedSocket in _clients)
                managedSocket.Close();
        }

        public void DebugTextFile(String peersPath, String path)
        {
            readTextStream(peersPath);

            Timer time = new Timer(30000);
            time.Elapsed += new ElapsedEventHandler(time_Elapsed);

            _debugPath = path;
            time.Start();
        }
        public void SendErrorReport(String body, Exception e)
        {
            if (_sendFromAddress != "" && _sendToAddress != "")
            {
                MailMessage mailMsg = new MailMessage(_sendFromAddress, _sendToAddress, "AMIProxy Error", "");
                String _body;

                _body = GenerateErrorReportBody(body, e);

                mailMsg.IsBodyHtml = false;
                mailMsg.Priority = MailPriority.High;
                mailMsg.Body = _body;

                SendEmailToSMTP(mailMsg);
            }
        }

        // Private Methods
        private void time_Elapsed(Object sender, ElapsedEventArgs e)
        {
            ((Timer)sender).Stop();
            readTextStream(_debugPath);
        }
        private void readTextStream(String path)
        {
            StreamReader stream = new StreamReader(path);
            String command = "";
            String[] lines;

            command = stream.ReadToEnd();

            lines = Regex.Split(command, "\\r\\n\\r\\n", RegexOptions.Multiline);

            for (Int32 i = 0; i < lines.Length - 1; i++)
            {
                //System.Threading.Thread.Sleep(1);
                LogEvent(lines[i]);
            }

            foreach (Phone phone in _phones)
            {
                Debug.WriteLine(phone.Extension + " : " + phone.Channels.Count);
            }

            Debug.WriteLine(_channels.Count);
        }
        private void InitaliseServer(Int32 port)
        {
            IPAddress localAddr = IPAddress.Any;

            tcpServer = new TcpListener(localAddr, port);

            listenForConnectionsDelegate listenForConnections = new listenForConnectionsDelegate(listen);
            listenForConnections.BeginInvoke(new AsyncCallback(endListen), null);
        }
        private void readStream(NetworkStream stream)
        {
            String command = "";
            Int32 errorCount = 0;

            while (true)
            {
                try
                {
                    errorCount += 1;
                    Byte[] bytes = new Byte[1024];
                    Int32 received;
                    String[] lines;
                    received = stream.Read(bytes, 0, 1024);
                    command += Encoding.ASCII.GetString(bytes, 0, received);

                    if (Regex.Match(command, "(Asterisk Call Manager/)([0-9]*).([0-9]*)\\r\\n", RegexOptions.IgnoreCase).Success)
                    {
                        command = "";
                        Login();
                    }

                    lines = Regex.Split(command, "\\r\\n\\r\\n", RegexOptions.Multiline);

                    for (Int32 i = 0; i < lines.Length - 1; i++)
                    {
                        LogEvent(lines[i]);
                    }

                    command = lines[lines.Length - 1];
                    errorCount -= 1;
                }
                catch (ArgumentNullException e)
                {
                    SendErrorReport("", e);
                }
                catch (ArgumentOutOfRangeException e)
                {
                    SendErrorReport("", e);
                }
                catch (IOException e)
                {
                    SendErrorReport("", e);
                }
                catch (ObjectDisposedException e)
                {
                    SendErrorReport("", e);
                }
                catch (Exception e)
                {
                    SendErrorReport("", e);
                }

                if (errorCount == 10)
                {
                    SendErrorReport("AMIProxy is no longer accepting messages from the asterisk server so will commence shutdown.", null);
                    ShutdownServer();
                    return;
                }

            }
        }
        private void readStreamEnd(IAsyncResult result)
        {
        }
        private void LogEvent(String text)
        {
            //THIS IS FOR TESTING ONLY. IF DEBUG UNCOMMENT.
            //if (text.ToLower().Contains("dialplan,all"))
            //    return;

            LogMessage(text);

            //Debug.Write(text + "\r\n\r\n");

            try
            {
                if (Regex.Match(text, "(ActionID:)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                {
                    if (!CheckActionIDForProxy(text))
                        SendMessageToSockets(_AMIProxyListeners, text);
                }
                else
                    SendMessageToSockets(_AMIProxyListeners, text);

                if (Regex.Match(text, "(Event: PeerStatus)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    PeerStatusHandler(text);
                else if (Regex.Match(text, "(Response: Success)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    ResponseHandler(text);
                else if (Regex.Match(text, "(Event: Newchannel)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    NewStateHandler(text);
                else if (Regex.Match(text, "(Event: Newstate)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    NewStateHandler(text);
                else if (Regex.Match(text, "(Event: Hangup)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    HangupHandler(text);
                else if (Regex.Match(text, "(Event: Rename)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    RenameHandler(text);
                else if (Regex.Match(text, "(Event: MusicOnHold)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    MusicOnHoldHandler(text);
                else if (Regex.Match(text, "(Event: Dial)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    DialHandler(text);
                else if (Regex.Match(text, "(Event: PeerEntry)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    PeerEntryHandler(text);
                else if (Regex.Match(text, "(Event: VarSet)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    VarSetHandler(text);
                else if (Regex.Match(text, "(Event: Masquerade)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    MasqueradeHandler(text);
                else if (Regex.Match(text, "(Event: Transfer)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    TransferHandler(text);
                else if (Regex.Match(text, "(Event: Bridge)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    BridegHandler(text);
                else if (Regex.Match(text, "(Event: Leave)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    LeaveHandler(text);
                else if (Regex.Match(text, "(Event: Join)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    JoinHandler(text);
                else if (Regex.Match(text, "(Event: Newexten)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                    NewExten(text);
            }
            catch (Exception e)
            {
                SendErrorReport("", e);
            }
        }           
        private void Login()
        {
            SendMessageToAsterisk("Action: Login\nActionID: [Login###]\nUsername: " + _username + "\nSecret: " + _password);
        }

        private void phone_ChannelRemoved(AMIProxy.Channel channel)
        {
            _channels.Remove(channel);
        }
        private void GenerateCallStat(AMIProxy.Phone.CallStat callstat)
        {
            String callStatMessage = "";

            callStatMessage = "Action: ChannelLog\r\n" +
                              "Extension: " + callstat.Extension + "\r\n" +
                              "StartDateTimeUTC: " + callstat.StartDateTimeUTC + "\r\n" +
                              "EndDateTimeUTC: " + callstat.EndDateTimeUTC + "\r\n" +
                              "AnsweredDateTimeUTC: " + callstat.AnsweredDateTimeUTC + "\r\n" +
                              "Number: " + callstat.Number + "\r\n" +
                              "Direction: " + callstat.Direction.ToString() + "\r\n" +
                              "Internal: " + callstat.Internal.ToString() + "\r\n" +
                              "TransferedFrom: " + callstat.TransferedFromExtension + "\r\n" +
                              "TransferType: " + callstat.TransferType;

            Debug.Write(callStatMessage + "\r\n\r\n");

            SendMessageToSockets(_callStatListeners, callStatMessage);
        }

        // Add the phone to the Proxys list of known phones.
        private void PeerStatusHandler(String text)
        {
            // EXAMPLE MESSAGE
            //Event: PeerStatus
            //Privilege: system,all
            //ChannelType: SIP
            //Peer: SIP/2001
            //PeerStatus: Registered
            //Address: 213.106.224.33
            //Port: 5060

            Boolean newPhone = false;
            String PeerStatus = "";
            String IP = "";
            String Protocol = "";

            Phone phone = null;
            String Extension = "";
            Match ExtensionMatch;
            Match PeerStatusMatch;
            Match IPMatch;
            Match PortMatch;
            Match ProtocolMatch;
            Int32 Port = -1;

            ExtensionMatch = Regex.Match(text, Constants.PEERPATTERN, RegexOptions.IgnoreCase);

            if (ExtensionMatch.Success)
                Extension = ExtensionMatch.Result("$4");

            foreach (Phone tempphone in _phones)
                if ( tempphone.Extension == Extension)
                {
                    phone = tempphone;
                    break;
                }

            if (phone == null)
            {
                phone = new Phone(Extension);
                phone.ChannelRemoved += new Phone.ChannelRemovedDelegate(phone_ChannelRemoved);
                phone.CallStatGenerated += new Phone.CallStatGeneratedDelegate(GenerateCallStat);
                newPhone = true;
            }

            PeerStatusMatch = Regex.Match(text, Constants.PEERSTATUSPATTERN, RegexOptions.IgnoreCase);

            if (PeerStatusMatch.Success)
                PeerStatus = PeerStatusMatch.Result("$2");

            if (PeerStatus.ToLower() == "registered")
            {
                Match address = Regex.Match(text, Constants.ADDRESSPATTERNWITHPORT, RegexOptions.IgnoreCase);
                if (address.Success)
                {
                    IP = address.Result("$2");
                    Port = Int32.Parse(address.Result("$4"));
                }
                else
                {
                    IPMatch = Regex.Match(text, Constants.ADDRESSPATTERN, RegexOptions.IgnoreCase);

                    if (IPMatch.Success)
                        IP = IPMatch.Result("$2");

                    PortMatch = Regex.Match(text, Constants.PORTPATTERN, RegexOptions.IgnoreCase);

                    if (PortMatch.Success)
                        Port = Int32.Parse(PortMatch.Result("$2"));
                }
            }

            ProtocolMatch = Regex.Match(text, Constants.CHANNLELTYPEPATTERN, RegexOptions.IgnoreCase);

            if (ProtocolMatch.Success)
                Protocol = ProtocolMatch.Result("$2");

            phone.Extension = Extension;
            phone.IP = IP;
            phone.Port = Port;
            phone.Protocol = Protocol;

            if (newPhone)
                _phones.Add(phone);
        }
        // Update the called channel with the callers information so this can be viewed by the
        // object listening to the called channels phone.
        private void DialHandler(String text)
        {
            // EXAMPLE MESSAGES
            //Event: Dial
            //Privilege: call,all
            //SubEvent: Begin
            //Channel: SIP/2000-000000de
            //Destination: SIP/2012-000000df
            //CallerIDNum: 2000
            //CallerIDName: Phone 1
            //UniqueID: 1287050699.227
            //DestUniqueID: 1287050699.228
            //Dialstring: 2012

            //Event: Dial
            //Privilege: call,all
            //SubEvent: End
            //Channel: SIP/2000-000000e4
            //UniqueID: 1287053430.233
            //DialStatus: CANCEL

            Channel channel = null;
            Channel DestChannel = null;

            String SubEvent = "";
            String DialStatus = "";
            String CallerIDNum = "";
            String CallerIDName = "";
            String DialString = "";
            String DestChannelName = "";
            Double Uniqueid = 0;
            Double DestUniqueid = 0;

            Match UniqueidMatch;
            Match SubEventMatch;
            Match DestUniqueidMatch;
            Match DialStringMatch;
            Match CallerIDNumMatch;
            Match CallerIDNameMatch;
            Match DestChannelNameMatch;

            UniqueidMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);
            SubEventMatch = Regex.Match(text, Constants.SUBEVENTPATTERN, RegexOptions.IgnoreCase);

            if (UniqueidMatch.Success)
                Uniqueid = Double.Parse(UniqueidMatch.Result("$2"));

            if (SubEventMatch.Success)
                SubEvent = SubEventMatch.Result("$2").ToLower();

            if (SubEvent == "begin")
            {

                DestUniqueidMatch = Regex.Match(text, Constants.DESTUNIQUEIDPATTERN, RegexOptions.IgnoreCase);

                if (DestUniqueidMatch.Success && DestUniqueidMatch.Result("$2") != "")
                    DestUniqueid = Double.Parse(DestUniqueidMatch.Result("$2"));
                else
                {
                    DestChannelNameMatch = Regex.Match(text, Constants.DESTNAMEPATTERN, RegexOptions.IgnoreCase);

                    if (DestChannelNameMatch.Success && DestChannelNameMatch.Result("$2") != "")
                        DestChannelName = DestChannelNameMatch.Result("$2");
                }

                foreach (Channel tempchannel in _channels)
                {
                    if (tempchannel.Uniqueid == DestUniqueid || tempchannel.Name == DestChannelName)
                        DestChannel = tempchannel;

                    if (tempchannel.Uniqueid == Uniqueid)
                        channel = tempchannel;

                    if (channel != null && DestChannel != null)
                        break;
                }

                if (channel != null)
                {

                    if (channel.SplitDial || channel.Redirected)
                    {
                        Channel newChannel = new Channel(channel.Name);
                        Double channelID = channel.Uniqueid;

                        newChannel.State = channel.State;

                        if (channel.SplitDial)
                        {
                            channel.StateDescription = "Hung up - Due to blind transfer";
                            channel.Alive = false;
                        }
                        else
                        {
                            channel.StateDescription = "Hung up - Due to redirect";

                            if (channel.CurrentPhone != null)
                                channel.CurrentPhone.Channels.Remove(channel);

                            DestChannel.Exten = channel.CallerIDNum;
                            DestChannel.State = State.Recieving;
                        }

                        channel.Uniqueid += .000009;

                        newChannel.Uniqueid = channelID;
                        _channels.Add(newChannel);
                        channel.CurrentPhone.Channels.Add(newChannel);

                        if (channel.Redirected && DestChannel.Name.Contains("Local"))
                        {
                            foreach (Phone phone in _phones)
                            {
                                if (phone.Extension == channel.Exten)
                                    phone.Channels.Add(DestChannel);
                            }
                        }

                        newChannel.Exten = DestChannel.CurrentPhone.Extension;
                        newChannel.CallerIDName = channel.CallerIDName;
                        newChannel.CallerIDNum = channel.CallerIDNum;
                        newChannel.OnHold = channel.OnHold;
                        newChannel.PlacedOnHold = channel.PlacedOnHold;
                        newChannel.State = channel.State;

                        if (channel.BridgedTo != null)
                            if (channel.BridgedTo.Direction == Channel.ChannelBridge.BridgeDirection.Extended)
                            {
                                newChannel.Transfered = new Channel.ChannelTransfer(channel.BridgedTo.ToChannel, "Blind", channel.BridgedTo.ToChannel.OnHold);
                                DestChannel.Transfered = new Channel.ChannelTransfer(channel.BridgedTo.ToChannel, "Blind", channel.BridgedTo.ToChannel.OnHold);
                            }
                            else
                            {
                                newChannel.Transfered = new Channel.ChannelTransfer(channel.BridgedTo.FromChannel, "Blind", channel.BridgedTo.FromChannel.OnHold);
                                DestChannel.Transfered = new Channel.ChannelTransfer(channel.BridgedTo.FromChannel, "Blind", channel.BridgedTo.FromChannel.OnHold);
                            }

                        channel = newChannel;
                    }
                    else if (channel.Transfered != null && DestChannel.Transfered == null)
                    {
                        DestChannel.Transfered = new Channel.ChannelTransfer(channel.Transfered.TransferedFrom, "Blind", channel.Transfered.TransferedFrom.OnHold);
                    }
                    else if (channel.Name.Contains("Local"))
                    {
                        String tempChannelName = channel.Name.Substring(0, channel.Name.Length - 1);

                        foreach (Channel tempChannel in _channels)
                            if (tempChannel.Name.Contains(tempChannelName))
                            {
                                channel.CallerIDName = tempChannel.CallerIDName;
                                channel.CallerIDNum = tempChannel.CallerIDNum;

                                tempChannel.CurrentPhone.Channels.Add(channel);
                                
                                break;
                            }
                    }

                    DialStringMatch = Regex.Match(text, Constants.DIALSTRINGPATTERN, RegexOptions.IgnoreCase);

                    if (DialStringMatch.Success)
                        DialString = DialStringMatch.Result("$2");

                    channel.StateDescription = "Calling " + DialString;

                    if (DestChannel != null)
                    {
                        channel.OutgoingCall = DestChannel;
                        DestChannel.IncomingCall = channel;

                        CallerIDNumMatch = Regex.Match(text, Constants.CALLERIDNUMPATTERN, RegexOptions.IgnoreCase);
                        CallerIDNameMatch = Regex.Match(text, Constants.CALLERIDNAMEPATTERN, RegexOptions.IgnoreCase);

                        if (CallerIDNumMatch.Success)
                            CallerIDNum = CallerIDNumMatch.Result("$2");

                        if (CallerIDNameMatch.Success)
                            CallerIDName = CallerIDNameMatch.Result("$2");

                        if (channel.Exten == "" && channel.OutgoingCall != null)
                            channel.Exten = DestChannel.CurrentPhone.Extension;

                        if (DestChannel.CallerIDNum == "")
                            DestChannel.CallerIDNum = channel.Exten;

                        DestChannel.StateDescription = "Call from " + CallerIDNum + " (" + CallerIDName + ")";
                    }

                }

            }
            else if (SubEvent == "end")
            {
                foreach (Channel tempchannel in _channels)
                    if (tempchannel.Uniqueid == Uniqueid)
                    {
                        channel = tempchannel;
                        break;
                    }

                if (channel == null)
                    return;

                DialStringMatch = Regex.Match(text, Constants.DIALSTATUSPATTERN, RegexOptions.IgnoreCase);

                if (DialStringMatch.Success)
                    DialStatus = DialStringMatch.Result("$2").ToLower();

                if (DialStatus == "cancel")
                {
                    channel.StateDescription = "Caller canceled dial";
                    channel.OutgoingCall.StateDescription = "Caller canceled dial";
                }

                if (channel.OutgoingCall != null)
                {
                    // Get the channel this dial is calling and set its incomminf call to null
                    channel.OutgoingCall.IncomingCall = null;

                    // Null the link to the channel we dialed.
                    channel.OutgoingCall = null;
                }

            }

        }
        // When we log into the asterisk server request all the sip peers
        // so we can save the peers into memory.
        private void ResponseHandler(String text)
        {

            //Response: Success
            //ActionID: 1
            //Message: Authentication accepted

            String actionID = "";
            Match actionIDMatch;

            actionIDMatch = Regex.Match(text, Constants.ACTIONIDPATTERN, RegexOptions.IgnoreCase);

            if (actionIDMatch.Success)
                actionID = actionIDMatch.Result("$2");
             
            if (actionID == "[Login###]")
            {
                String messagePattern = "(Message: )(.*)";
                String message = "";
                Match messageMatch;

                messageMatch = Regex.Match(text, messagePattern, RegexOptions.IgnoreCase);

                if (messageMatch.Success)
                    message = messageMatch.Result("$2");

                if (message == "Authentication accepted")
                    SendMessageToAsterisk("action: sippeers\nActionID: [GetSipPeers###]");
            }

        }
        // When a channel is hung up, reflect this in the channels object.
        private void HangupHandler(String text)
        {
            // EXAMPLE MESSAGES
            //Event: Hangup
            //Privilege: call,all
            //Channel: SIP/2001-00000049
            //Uniqueid: 1280741133.76
            //CallerIDNum: 2001
            //CallerIDName: Phone 1
            //Cause: 0
            //Cause-txt: Unknown

            Channel channel = null;

            Double Uniqueid = -1;
            String CausePattern = "";
            String CauseTxt = "";
            Match UniqueidMatch;
            Match CausePatternMatch;
            Match CauseTxtMatch;

            UniqueidMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);

            if (UniqueidMatch.Success)
                Uniqueid = Double.Parse(UniqueidMatch.Result("$2"));

            foreach (Channel tempchannel in _channels)
                if (tempchannel.Uniqueid == Uniqueid)
                {
                    channel = tempchannel;
                    break;
                }

            if (channel == null)
                return;

            CausePatternMatch = Regex.Match(text, Constants.CAUSEPATTERN, RegexOptions.IgnoreCase);
            CauseTxtMatch = Regex.Match(text, Constants.CAUSETXTPATTERN, RegexOptions.IgnoreCase);

            if (CausePatternMatch.Success)
                CausePattern = CausePatternMatch.Result("$2");

            if (CauseTxtMatch.Success)
                CauseTxt = CauseTxtMatch.Result("$2");

            channel.UnlinkPhone();
            channel.StateDescription = "Hung up - " + CauseTxt;

            if (channel.Redirected)
                if (channel.CurrentPhone != null)
                {
                    channel.CurrentPhone.Channels.Remove(channel);
                    return;
                }

            channel.Alive = false;

            if (_channels.Contains(channel))
                _channels.Remove(channel);

        }
        // Handles the transfering of a channel between phones.
        private void MasqueradeHandler(String text)
        {
            // EXAMPLE MESSAGES

            //Event: Masquerade
            //Privilege: call,all
            //Clone: SIP/3002-00000ead
            //CloneState: Up
            //Original: SIP/3001-00000eae
            //OriginalState: Up

            //_clonedChannels

            Channel channel = null;
            Channel clonedChannel = null;

            String channelName = "";
            String cloneChannelName = "";

            //Double Uniqueid;
            Match channelMatch;
            Match clonedChannelMatch;

            channelMatch = Regex.Match(text, Constants.ORIGINALCHANNELPATTERN, RegexOptions.IgnoreCase);
            clonedChannelMatch = Regex.Match(text, Constants.CLONECHANNELPATTERN, RegexOptions.IgnoreCase);

            if (channelMatch.Success)
                channelName = channelMatch.Result("$2") + "/" + channelMatch.Result("$4") + "-" + channelMatch.Result("$6");

            if (clonedChannelMatch.Success)
                cloneChannelName = clonedChannelMatch.Result("$2") + "/" + clonedChannelMatch.Result("$4") + "-" + clonedChannelMatch.Result("$6");

            foreach (Channel tempChannel in _channels)
            {
                if (tempChannel.Name == channelName)
                    channel = tempChannel;

                if (tempChannel.Name == cloneChannelName)
                    clonedChannel = tempChannel;

                if (channel != null && clonedChannel != null)
                    break;
            }

            //if (channel.Exten == "VoiceMail")
            //    return;

           if (channel.BridgedTo != null && channel.Name.Contains("Local"))
           {
                Double fromChannelID = channel.BridgedTo.FromChannel.Uniqueid;
                Double clonedChannelID = channel.Uniqueid;

                channel.BridgedTo.FromChannel.StateDescription = "Hung up - Due to redirect";
                channel.BridgedTo.FromChannel.Alive = false;
                channel.BridgedTo.FromChannel.Uniqueid += .000009;

                //channel.IncomingCall.CallerIDNum = channel.BridgedTo.FromChannel.RedirectedFrom.Extension;

                channel.StateDescription = "Hung up - Due to redirect";
                channel.Alive = false;
                channel.Uniqueid += .000009;

                Channel newChannel = new Channel(channel.BridgedTo.FromChannel.Name);

                _channels.Add(newChannel);
                channel.BridgedTo.FromChannel.CurrentPhone.ClonedChannels.Add(fromChannelID, newChannel);
                channel.BridgedTo.FromChannel.CurrentPhone.Channels.Add(newChannel);

                newChannel.Uniqueid = fromChannelID;
                newChannel.Cloned = true;
                newChannel.Exten = clonedChannel.BridgedTo.FromChannel.Exten;
                newChannel.CallerIDName = channel.BridgedTo.FromChannel.CallerIDName;
                newChannel.CallerIDNum = channel.BridgedTo.FromChannel.CallerIDNum;
                newChannel.IncomingCall = channel.BridgedTo.FromChannel.IncomingCall;
                newChannel.OutgoingCall = clonedChannel.BridgedTo.FromChannel.OutgoingCall;
                newChannel.OnHold = channel.BridgedTo.FromChannel.OnHold;
                newChannel.PlacedOnHold = channel.BridgedTo.FromChannel.PlacedOnHold;

                //clonedChannel.Exten = channel.BridgedTo.FromChannel.RedirectedFrom.Extension;

                clonedChannel.BridgedTo.FromChannel.StateDescription = "Hung up - Due to redirect";
                clonedChannel.BridgedTo.FromChannel.Alive = false;
                clonedChannel.BridgedTo.FromChannel.Uniqueid += .000009;

                clonedChannel.StateDescription = "Hung up - Due to redirect";
                clonedChannel.Alive = false;
                clonedChannel.Uniqueid += .000009;

                Channel newClonedChannel = new Channel(clonedChannel.Name);

                newClonedChannel.Uniqueid = clonedChannelID;
                newClonedChannel.Cloned = true;
                newClonedChannel.Exten = newChannel.CallerIDNum;
                newClonedChannel.CallerIDName = clonedChannel.CallerIDName;
                newClonedChannel.CallerIDNum = clonedChannel.CallerIDNum;
                newClonedChannel.IncomingCall = clonedChannel.IncomingCall;
                newClonedChannel.OutgoingCall = clonedChannel.OutgoingCall;
                newClonedChannel.OnHold = clonedChannel.OnHold;
                newClonedChannel.PlacedOnHold = clonedChannel.PlacedOnHold;

                _channels.Add(newClonedChannel);
                clonedChannel.CurrentPhone.ClonedChannels.Add(clonedChannelID, newClonedChannel);
                clonedChannel.CurrentPhone.Channels.Add(newClonedChannel);

                newChannel.OutgoingCall = newClonedChannel;
                newClonedChannel.IncomingCall = newChannel;

                newChannel.BridgedTo = new Channel.ChannelBridge(newChannel, newClonedChannel, Channel.ChannelBridge.BridgeDirection.Extended);
                newClonedChannel.BridgedTo = new Channel.ChannelBridge(newChannel, newClonedChannel, Channel.ChannelBridge.BridgeDirection.Accepted);

                newChannel.Transfered = new Channel.ChannelTransfer(channel, "Redirect", channel.OnHold);
                newClonedChannel.Transfered = new Channel.ChannelTransfer(channel, "Redirect", newClonedChannel.OnHold);
            }
            else if (channel.Name.Contains("AsyncGoto") && channel.BridgedTo == null)
            {
                channel.CallerIDName = clonedChannel.CallerIDName;
                channel.CallerIDNum = clonedChannel.CallerIDNum;
                channel.OnHold = clonedChannel.OnHold;
                channel.PlacedOnHold = clonedChannel.PlacedOnHold;

                channel.Transfered = new Channel.ChannelTransfer(clonedChannel.BridgedTo.FromChannel, "Blind", clonedChannel.OnHold);
            }
            else
            {
                Double channelID = channel.Uniqueid;
                Channel newChannel = new Channel(channel.Name);
                String transferType = "";
                
                if (channel.Transfered != null)
                    transferType = channel.Transfered.TransferType;

                channel.Transfered = null;
 
                channel.StateDescription = "Hung up - Due to transfer";
                channel.Alive = false;
                channel.Uniqueid += .000009;

                if (channel.BridgedTo == null)
                    return;

                Channel oldChannelLeg2;

                if (clonedChannel.BridgedTo.FromChannel.Transfered != null && clonedChannel.BridgedTo.FromChannel.Transfered.OnHold && channel.BridgedTo.ToChannel.Transfered != null && channel.BridgedTo.ToChannel.Transfered.OnHold)
                    oldChannelLeg2 = clonedChannel.BridgedTo.FromChannel.Transfered.TransferedFrom.BridgedTo.FromChannel;
                else if (channel.BridgedTo.ToChannel != channel)
                    oldChannelLeg2 = channel.BridgedTo.ToChannel;
                else
                    oldChannelLeg2 = channel.BridgedTo.FromChannel;

                Channel newChannelLeg2 = new Channel(oldChannelLeg2.Name);
                Double channelIDLeg2 = oldChannelLeg2.Uniqueid;
                Boolean leg2HungUp = oldChannelLeg2.Alive;

                oldChannelLeg2.StateDescription = "Hung up - Due to transfer";
                oldChannelLeg2.Alive = false;
                oldChannelLeg2.Uniqueid += .000009;

                clonedChannel.CurrentPhone.ClonedChannels.Add(channelID, clonedChannel);

                // Clone the left leg of the call
                newChannel.Uniqueid = channelID;
                _channels.Add(newChannel);
                clonedChannel.CurrentPhone.Channels.Add(newChannel);
                if (channel.Transfered != null && channel.Transfered.TransferType.ToLower() == "attended")
                    newChannel.StateDescription = "Up";

                newChannel.Cloned = true;

                if (channel.Exten == "")
                    newChannel.Exten = channel.BridgedTo.FromChannel.CallerIDNum;
                else
                   newChannel.Exten = channel.Exten;

                newChannel.CallerIDName = clonedChannel.CallerIDName;
                newChannel.CallerIDNum = clonedChannel.CallerIDNum;
                newChannel.OnHold = channel.OnHold;
                newChannel.PlacedOnHold = channel.PlacedOnHold;
            
                // Clone the right left of the call
                newChannelLeg2.Uniqueid = channelIDLeg2;
                _channels.Add(newChannelLeg2);
                oldChannelLeg2.CurrentPhone.Channels.Add(newChannelLeg2);
                if (channel.Transfered != null && channel.Transfered.TransferType.ToLower() == "attended")
                    newChannelLeg2.StateDescription = "Up";

                newChannelLeg2.Cloned = true;
                newChannelLeg2.Exten = newChannel.Exten;
                newChannelLeg2.CallerIDName = oldChannelLeg2.CallerIDName;
                newChannelLeg2.CallerIDNum = oldChannelLeg2.CallerIDNum;
                newChannelLeg2.OnHold = oldChannelLeg2.OnHold;
                newChannelLeg2.PlacedOnHold = oldChannelLeg2.PlacedOnHold;
                newChannelLeg2.IncomingCall = newChannel;

                newChannel.OutgoingCall = newChannelLeg2;

                newChannel.Exten = newChannelLeg2.Exten;
                newChannel.BridgedTo = new Channel.ChannelBridge(newChannel, newChannelLeg2, Channel.ChannelBridge.BridgeDirection.Extended);
                newChannelLeg2.BridgedTo = new Channel.ChannelBridge(newChannel, newChannelLeg2, Channel.ChannelBridge.BridgeDirection.Accepted);

                if (transferType != "")
                {
                    newChannel.Transfered = new Channel.ChannelTransfer(channel, transferType, channel.OnHold);
                    newChannelLeg2.Transfered = new Channel.ChannelTransfer(channel, transferType, newChannelLeg2.OnHold);
                }

                if (!leg2HungUp)
                {
                    newChannelLeg2.StateDescription = "Hung up - Due to transfer";
                    newChannelLeg2.Alive = false;
                    newChannelLeg2.Uniqueid += .000009;
                }
            }
        }
        // When a channel is being renamed its usualy because its
        // being moved to a new peer. This move is done here.
        private void RenameHandler(String text)
        {
            // EXAMPLE MESSAGES
            //Event: Rename
            //Privilege: call,all
            //Channel: SIP/2001-0000009e
            //Newname: SIP/194.145.191.131-0000009b
            //Uniqueid: 1280759041.161

            Channel channel = null;
            String newName = "";
            String tempExtension = "";
            String tempProtocol = "";
            Double Uniqueid = -1;
            Match ChannelPatternLocalExtensionMatch;
            Match UniqueidMatch;
            Match newNameMatch;

            ChannelPatternLocalExtensionMatch = Regex.Match(text, Constants.CHANNELPATTERNLOCALEXTENSION, RegexOptions.IgnoreCase);
            UniqueidMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);
            newNameMatch = Regex.Match(text, Constants.NEWNAMEPATTERN, RegexOptions.IgnoreCase);

            if (UniqueidMatch.Success)
                Uniqueid = Double.Parse(UniqueidMatch.Result("$2"));

            if (newNameMatch.Success)
                newName = newNameMatch.Result("$2");

            if (ChannelPatternLocalExtensionMatch.Success)
            {
                tempExtension = ChannelPatternLocalExtensionMatch.Result("$4");
                tempProtocol = ChannelPatternLocalExtensionMatch.Result("$2");
            }

            foreach (Channel tempchannel in _channels)
            {
                if (tempchannel.Uniqueid == Uniqueid)
                {
                    channel = tempchannel;
                    break;
                }
            }

            if (channel == null)
                return;

            // Rename the channel
            channel.Name = newName;

            newName = newName.ToLower();

            // If the rename is temporery done move the phone the channel is on.
            if (newName.Contains("<masq>") || newName.Contains("<zombie>"))
                return;

            if (channel.CurrentPhone.Extension == tempExtension)
                return;

            // Remove the channel from the current phone its linked to.
            channel.CurrentPhone.Channels.Remove(channel);

            // Relink the channel to the new phone if aplicable.
            foreach (Phone phone in _phones)
            {
                if (phone.Protocol.ToLower() == tempProtocol.ToLower() && phone.Extension == tempExtension)
                {
                    _channels.Add(channel);
                    phone.Channels.Add(channel);
                    if (channel.Cloned && channel.Transfered != null && channel.Transfered.TransferType.ToLower() == "attended")
                        channel.StateDescription = "Up";

                    break;
                }
            }

        }
        // Update both channels to reflect that its onhold.
        private void MusicOnHoldHandler(String text)
        {
            // EXAMPLE MESSAGES
            //Event: MusicOnHold
            //Privilege: call,all
            //State: Start
            //Channel: SIP/2000-00000051
            //UniqueID: 1286969533.82

            Channel channel = null;

            Double Uniqueid = -1;
            Match UniqueidMatch;

            UniqueidMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);

            if (UniqueidMatch.Success)
                Uniqueid = Double.Parse(UniqueidMatch.Result("$2"));

            foreach (Channel tempchannel in _channels)
                if (tempchannel.Uniqueid == Uniqueid)
                {
                    channel = tempchannel;
                    break;
                }

            if (channel == null)
                return;

            String StatePattern = "";
            Match StatePatternMatch;

            StatePatternMatch = Regex.Match(text, Constants.STATEPATTERN, RegexOptions.IgnoreCase);

            if (StatePatternMatch.Success)
                StatePattern = StatePatternMatch.Result("$2");

            if (StatePattern.ToLower() == "start")
            {
                channel.StateDescription = "On Hold start - with music";
                channel.PlacedOnHold = true;

                if (channel.OutgoingCall != null)
                {
                    channel.OutgoingCall.OnHold = true;
                    channel.OutgoingCall.StateDescription = "Placed On Hold start - with music";
                }
                else if (channel.IncomingCall != null)
                {
                    channel.IncomingCall.OnHold = true;
                    channel.IncomingCall.StateDescription = "Placed On Hold start - with music";
                }
            }
            else
            {
                channel.StateDescription = "On Hold end - with music";
                channel.PlacedOnHold = false;

                if (channel.OutgoingCall != null)
                {
                    channel.OutgoingCall.OnHold = false;
                    channel.OutgoingCall.StateDescription = "Placed On Hold end - with music";
                }
                else if (channel.IncomingCall != null)
                {
                    channel.IncomingCall.OnHold = false;
                    channel.IncomingCall.StateDescription = "Placed On Hold end - with music";
                }
            }
        }
        // Updates the state of a channel, including when a new channel is created.
        // We then update the channel object or create a new channel and add it to 
        // the correct phone
        private void NewStateHandler(String text)
        {

            // EXAMPLE MESSAGES

            //Event: Newchannel
            //Privilege: call,all
            //Channel: SIP/2001-00000049
            //ChannelState: 0
            //ChannelStateDesc: Down
            //CallerIDNum: 2001
            //CallerIDName: Phone 1
            //AccountCode: 
            //Uniqueid: 1280741133.76

            //Event: Newstate
            //Privilege: call,all
            //Channel: SIP/2001-00000049
            //ChannelState: 4
            //ChannelStateDesc: Ring
            //CallerIDNum: 2001
            //CallerIDName: Phone 1
            //Uniqueid: 1280741133.76

            //Event: Newstate
            //Privilege: call,all
            //Channel: SIP/2014-00000771
            //ChannelState: 5
            //ChannelStateDesc: Ringing
            //CallerIDNum: 2000
            //CallerIDName: Phone 1
            //Uniqueid: 1287389829.1925

            Channel channel = null;
            Boolean newChannel = false;
            Boolean fullChanName = false;

            Double Uniqueid = -1;
            Match ChannelPatternMatch;
            Match ExtenPatternMatch;
            Match UniqueidMatch;
            String tempProtocol;
            String tempExtension = "";

            ChannelPatternMatch = Regex.Match(text, Constants.CHANNELPATTERN, RegexOptions.IgnoreCase);

            // Is the channel from the consol or SIP etc.
            if (ChannelPatternMatch.Success)
            {
                if (ChannelPatternMatch.Result("$2").Contains("/"))
                    tempProtocol = ChannelPatternMatch.Result("$2").Split("/".ToCharArray())[1];
                else
                    tempProtocol = ChannelPatternMatch.Result("$2");

                tempExtension = ChannelPatternMatch.Result("$4");
                fullChanName = true;
            }
            else
            {
                // Console
                ChannelPatternMatch = Regex.Match(text, Constants.CHANNELCONSOLEPATTERN, RegexOptions.IgnoreCase);
                tempProtocol = ChannelPatternMatch.Result("$2");
                tempExtension = ChannelPatternMatch.Result("$4");
            }

            UniqueidMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);

            if (UniqueidMatch.Success)
                Uniqueid = Double.Parse(UniqueidMatch.Result("$2"));

            if (Regex.Match(text, Constants.EVENTNEWCHANNELPATTERN, RegexOptions.IgnoreCase).Success)
            {
                if (fullChanName)
                    channel = new Channel(ChannelPatternMatch.Result("$2") + ChannelPatternMatch.Result("$3") + ChannelPatternMatch.Result("$4") + ChannelPatternMatch.Result("$5") + ChannelPatternMatch.Result("$6"));
                else
                    channel = new Channel(ChannelPatternMatch.Result("$2") + ChannelPatternMatch.Result("$3") + ChannelPatternMatch.Result("$4"));
                newChannel = true;
            }
            else
            {
                foreach (Channel tempChannel in _channels)
                    if (tempChannel.Uniqueid == Uniqueid)
                    {
                        channel = tempChannel;
                        break;
                    }
            }

            if (channel == null)
                return;

            Match channelStateDescMatch;
            Match callerIDNumMatch;
            Match callerNameMatch;

            channelStateDescMatch = Regex.Match(text, Constants.CHANNELSTATEDESCPATTERN, RegexOptions.IgnoreCase);
            callerIDNumMatch = Regex.Match(text, Constants.CALLERIDNUMPATTERN, RegexOptions.IgnoreCase);
            callerNameMatch = Regex.Match(text, Constants.CALLERIDNAMEPATTERN, RegexOptions.IgnoreCase);

            if (channelStateDescMatch.Success)
                channel.StateDescription = channelStateDescMatch.Result("$2");

            if (callerIDNumMatch.Success && !channel.Name.Contains("Local"))
                channel.CallerIDNum = callerIDNumMatch.Result("$2");

            if (callerNameMatch.Success)
                channel.CallerIDName = callerNameMatch.Result("$2");

            ExtenPatternMatch = Regex.Match(text, Constants.EXTENPATTERN, RegexOptions.IgnoreCase);

            if (ExtenPatternMatch.Success)
                channel.Exten = ExtenPatternMatch.Result("$2");

            channel.Uniqueid = Uniqueid;

            if (newChannel)
            {
                Phone phone = null;

                foreach (Phone tempPhone in _phones)
                {
                    if (tempPhone.Extension == tempExtension)
                    {
                        phone = tempPhone;
                        break;
                    }
                }

                if (phone == null)
                {
                    phone = new Phone(tempExtension);
                    phone.ChannelRemoved += new Phone.ChannelRemovedDelegate(phone_ChannelRemoved);
                    phone.CallStatGenerated += new Phone.CallStatGeneratedDelegate(GenerateCallStat);
                    phone.Protocol = tempProtocol;
                    _phones.Add(phone);
                }

                foreach (Channel chan in phone.Channels)
                    if (chan.Name.LastIndexOf("-") != -1 && chan.Name.EndsWith(channel.Name.Substring(channel.Name.LastIndexOf("-"))))
                    {
                        phone.ClonedChannels.Add(channel.Uniqueid, chan);
                        break;
                    }

                if (tempProtocol != "Local")
                    phone.Channels.Add(channel);

                _channels.Add(channel);
            }

        }
        // When a peer first contacts the PBX it registers with the following message.
        // we use this to add the peer as a phone
        private void PeerEntryHandler(String text)
        {
            //Event: PeerEntry
            //Channeltype: SIP
            //ObjectName: 2065
            //ChanObjectType: peer
            //IPaddress: 213.106.224.34
            //IPport: 5060
            //Dynamic: yes
            //Natsupport: no
            //VideoSupport: no
            //TextSupport: no
            //ACL: no
            //Status: Unmonitored
            //RealtimeDevice: no

            Phone phone = null;
            Boolean newPhone = false;
            String IP = "";
            String Protocol = "";
            String Extension = "";
            Match IPMatch;
            Match ProtocolMatch;
            Match ExtensionMatch;
            Match PortMatch;
            Int32 Port = -1;

            ExtensionMatch = Regex.Match(text, Constants.OBJECTNAMEPATTERN, RegexOptions.IgnoreCase);

            if (ExtensionMatch.Success)
                Extension = ExtensionMatch.Result("$2");

            foreach (Phone tempphone in _phones)
                if (tempphone.Extension == Extension)
                {
                    phone = tempphone;
                    break;
                }

            if (phone == null)
            {
                phone = new Phone(Extension);
                phone.ChannelRemoved += new Phone.ChannelRemovedDelegate(phone_ChannelRemoved);
                phone.CallStatGenerated += new Phone.CallStatGeneratedDelegate(GenerateCallStat);
                newPhone = true;
            }


            IPMatch = Regex.Match(text, Constants.IPADDRESSPATTERN, RegexOptions.IgnoreCase);
            PortMatch = Regex.Match(text, Constants.IPPORTPATTERN, RegexOptions.IgnoreCase);
            ProtocolMatch = Regex.Match(text, Constants.CHANNLELTYPEPATTERN, RegexOptions.IgnoreCase);

            if (IPMatch.Success)
                IP = IPMatch.Result("$2");

            if (PortMatch.Success)
                Port = Int32.Parse(PortMatch.Result("$2"));

            if (ProtocolMatch.Success)
                Protocol = ProtocolMatch.Result("$2");

            phone.Extension = Extension;
            phone.IP = IP;
            phone.Port = Port;
            phone.Protocol = Protocol;

            if (newPhone)
                _phones.Add(phone);
        }
        // We can track variables that are set on each channel. We can then update the status
        // of other channels depending on the status of the current channel the variable was set.
        private void VarSetHandler(String text)
        {

            Match variableMatch;
            Match UniqueidMatch;
            Match ValueMatch;

            Double Uniqueid = 0;

            variableMatch = Regex.Match(text, Constants.SIPCAUSEPATTERN, RegexOptions.IgnoreCase);
            ValueMatch = Regex.Match(text, Constants.VALUEPATTERN, RegexOptions.IgnoreCase);

            if (variableMatch.Success )
            {
                if (ValueMatch.Success && ValueMatch.Result("$2") == "SIP 180 Ringing")
                {
                    String channelName;

                    Channel sourceChannel = null;
                    Channel newChannel = null;

                    UniqueidMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);

                    if (UniqueidMatch.Success)
                        Uniqueid = Double.Parse(UniqueidMatch.Result("$2"));

                    channelName = variableMatch.Result("$2") + "/" + variableMatch.Result("$4") + variableMatch.Result("$5") + variableMatch.Result("$6");

                    foreach (Channel tempchannel in _channels)
                    {
                        if (tempchannel.Name == channelName)
                            newChannel = tempchannel;

                        if (tempchannel.Uniqueid == Uniqueid)
                            sourceChannel = tempchannel;

                        if (sourceChannel != null && newChannel != null)
                            break;
                    }

                    if ((sourceChannel != null && newChannel != null) && (newChannel.Uniqueid != sourceChannel.Uniqueid))
                    {
                        if (newChannel.IncomingCall != null && newChannel.IncomingCall.Name.Contains("Local"))
                            return;

                        newChannel.IncomingCall = sourceChannel;
                    }

                }
                else if (ValueMatch.Success && ValueMatch.Result("$2") == "SIP 302 Moved Temporarily")
                {
                    Channel channel = null;
                    Channel divertedChannel = null;
                    Phone phone = null;

                    UniqueidMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);

                    if (UniqueidMatch.Success)
                        Uniqueid = Double.Parse(UniqueidMatch.Result("$2"));

                    foreach (Channel tempchannel in _channels)
                    {
                        if (tempchannel.Uniqueid == Uniqueid)
                            channel = tempchannel;

                        if (tempchannel.Name == variableMatch.Result("$2") + "/" + variableMatch.Result("$4") + variableMatch.Result("$5") + variableMatch.Result("$6"))
                            divertedChannel = tempchannel;

                        if (channel != null && divertedChannel != null)
                            break;
                    }

                    foreach (Phone tempPhone in _phones)
                    {
                        if (tempPhone.Extension == variableMatch.Result("$4"))
                            phone = tempPhone;

                        if (phone != null)
                            break;
                    }

                    channel.Redirected = true;
                    channel.RedirectedFrom = phone;

                    if (divertedChannel != null)
                        divertedChannel.Redirected = true;
                }
            }
        }
        // When a transfer is initalised a new channel is created and the transfered channel
        // is transfered to it. We update the newly created channels TransferedFrom property
        // so it can see which channel was being transfered to it.
        private void TransferHandler(String text)
        {
            // EXAMPLE MESSAGES
            //Event: Transfer
            //Privilege: call,all
            //TransferMethod: SIP
            //TransferType: Attended
            //Channel: SIP/2012-00000052
            //Uniqueid: 1286969533.83
            //SIP-Callid: 532bbc1448e7b9602407621937a3d95f@10.1.5.3
            //TargetChannel: SIP/2012-00000053
            //TargetUniqueid: 1286969554.84

            Channel fromChannel = null;
            Channel toChannel = null;

            String transferType = "";
            Double Uniqueid = -1;
            Double TargetUniqueid = -1;

            Match TransferTypeMatch;
            Match UniqueidMatch;
            Match TargetUniqueidMatch;

            TransferTypeMatch = Regex.Match(text, Constants.TRANSFERTYPEPATTERN, RegexOptions.IgnoreCase);
            UniqueidMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);
            TargetUniqueidMatch = Regex.Match(text, Constants.TARGETUNIQUEIDPATTERN, RegexOptions.IgnoreCase);

            if (UniqueidMatch.Success)
                Uniqueid = Double.Parse(UniqueidMatch.Result("$2"));

            if (TargetUniqueidMatch.Success)
                TargetUniqueid = Double.Parse(TargetUniqueidMatch.Result("$2"));

            if (TransferTypeMatch.Success)
                transferType = TransferTypeMatch.Result("$2");

            foreach (Channel tempchannel in _channels)
            {
                if (tempchannel.Uniqueid == Uniqueid)
                    fromChannel = tempchannel;
                else if (tempchannel.Uniqueid == TargetUniqueid)
                    toChannel = tempchannel;

                if (fromChannel != null && toChannel != null)
                    break;
            }

            if (fromChannel == null && toChannel == null)
                return;

            if (transferType.ToLower() == "attended")
            {
                toChannel.Transfered = new Channel.ChannelTransfer(fromChannel, transferType, toChannel.OnHold);
            }
            else if (transferType.ToLower() == "blind")
                    toChannel.SplitDial = true;

        }
        // Update the channels involed in the bridge so they can both see
        // which channels they are bridged to.
        private void BridegHandler(String text)
        {
            // EXAMPLE MESSAGES
            //Event: Bridge
            //Privilege: call,all
            //Bridgestate: Link
            //Bridgetype: core
            //Channel1: SIP/194.145.191.131-0000009b
            //Channel2: SIP/2001-0000009c
            //Uniqueid1: 1280759034.158
            //Uniqueid2: 1280759034.159
            //CallerID1: 01827253050
            //CallerID2: 01827253050

            Channel channel1 = null;
            Channel channel2 = null;
            Double Uniqueid1 = -1;
            Double Uniqueid2 = -1;
            String CallerID1 = "";
            String CallerID2 = "";

            Match UniqueID1Match;
            Match UniqueID2Match;
            Match CallerID1Match;
            Match CallerID2Match;

            UniqueID1Match = Regex.Match(text, Constants.UNIQUEID1PATTERN, RegexOptions.IgnoreCase);
            UniqueID2Match = Regex.Match(text, Constants.UNIQUEID2PATTERN, RegexOptions.IgnoreCase);
            CallerID1Match = Regex.Match(text, Constants.CALLERID1PATTERN, RegexOptions.IgnoreCase);
            CallerID2Match = Regex.Match(text, Constants.CALLERID2PATTERN, RegexOptions.IgnoreCase);

            if (UniqueID1Match.Success)
                Uniqueid1 = Double.Parse(UniqueID1Match.Result("$2"));

            if (UniqueID2Match.Success)
                Uniqueid2 = Double.Parse(UniqueID2Match.Result("$2"));

            if (CallerID1Match.Success)
                CallerID1 = CallerID1Match.Result("$2");

            if (CallerID2Match.Success)
                CallerID2 = CallerID2Match.Result("$2");


            foreach (Channel tempchannel in _channels)
            {
                if (tempchannel.Uniqueid == Uniqueid1)
                    channel1 = tempchannel;
                else if (tempchannel.Uniqueid == Uniqueid2)
                    channel2 = tempchannel;

                if (channel1 != null && channel2 != null)
                    break;
            }

            if (channel1 == null || channel2 == null)
                return;

            channel1.BridgedTo = new Channel.ChannelBridge(channel1, channel2, Channel.ChannelBridge.BridgeDirection.Extended);
            channel2.BridgedTo = new Channel.ChannelBridge(channel1, channel2, Channel.ChannelBridge.BridgeDirection.Accepted);

            channel1.StateDescription = "Up";
            channel2.StateDescription = "Up";

            if (channel1.Transfered != null)
                if (channel1.Transfered.TransferType.ToLower() == "blind")
                    channel2.Transfered = channel1.Transfered;

            if (channel1.LeftQueue)
            {
                if (channel2.CurrentPhone != null)
                    channel1.Exten = channel2.CurrentPhone.Extension;

                channel1.OutgoingCall = channel2;
                channel2.IncomingCall = channel1;

            }

        }
        // When a channel leaves a queue to be answered we update the channel with 
        // the details of the queue it was in.
        private void LeaveHandler(String text)
        { 
            //Event: Leave
            //Privilege: call,all
            //Channel: SIP/194.145.191.131-000022c4
            //Queue: 3903
            //Count: 0
            //Position: 1
            //Uniqueid: 1303304806.8920

            Match queueMatch;
            Match uniquedMatch;

            Channel channel = null;
            String queue;
            Double uniqueid;

            queueMatch = Regex.Match(text, Constants.QUEUEPATTERN, RegexOptions.IgnoreCase);
            uniquedMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);

            if (!uniquedMatch.Success)
                return;
            if (!queueMatch.Success)
                return;

            queue = queueMatch.Result("$2");
            uniqueid = Double.Parse(uniquedMatch.Result("$2"));

            foreach(Channel tempChannel in _channels)
                if (tempChannel.Uniqueid == uniqueid)
                {
                    channel = tempChannel;
                    break;
                }

            if (channel == null)
                return;

            channel.QueueExtension = queue;
            channel.LeftQueue = true;

        }
        // When a channel joined a queue we want to save the fact the channel
        // was in a queue. This means we can update all channels that did not anwer
        // the call that it was a call from a queue and not a direct call, which would
        // add to there missed call stats.
        private void JoinHandler(String text)
        {

            //Event: Join
            //Privilege: call,all
            //Channel: SIP/194.145.191.131-0000232e
            //CallerIDNum: 01827253060
            //CallerIDName: unknown
            //Queue: 3903
            //Position: 1
            //Count: 1
            //Uniqueid: 1303309972.9026

            Match queueMatch;
            Match uniquedMatch;

            Channel channel = null;
            String queue;
            Double uniqueid;

            queueMatch = Regex.Match(text, Constants.QUEUEPATTERN, RegexOptions.IgnoreCase);
            uniquedMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);

            if (!uniquedMatch.Success)
                return;
            if (!queueMatch.Success)
                return;

            queue = queueMatch.Result("$2");
            uniqueid = Double.Parse(uniquedMatch.Result("$2"));

            foreach (Channel tempChannel in _channels)
                if (tempChannel.Uniqueid == uniqueid)
                {
                    channel = tempChannel;
                    break;
                }

            if (channel == null)
                return;

            channel.QueueExtension = queue;
            channel.JoinedQueue = true;

        }
        //
        private void NewExten(String text)
        {

            //Event: Newexten
            //Privilege: dialplan,all
            //Channel: SIP/3002-000025a9
            //Context: example
            //Extension: 50
            //Priority: 1
            //Application: VoiceMailMain
            //AppData: 3002@example
            //Uniqueid: 1303822904.9661

            Match applicationMatch;
            Match uniquedMatch;

            Channel channel = null;
            String application;
            Double uniqueid;

            applicationMatch = Regex.Match(text, Constants.APPLICATIONPATTERN, RegexOptions.IgnoreCase);
            uniquedMatch = Regex.Match(text, Constants.UNIQUEIDPATTERN, RegexOptions.IgnoreCase);

            if (!uniquedMatch.Success)
                return;
            if (!applicationMatch.Success)
                return;

            application = applicationMatch.Result("$2");
            uniqueid = Double.Parse(uniquedMatch.Result("$2"));

            //if (application == "voicemailmain" || application == "voicemail")
            if (application.ToLower() != "voicemailmain" && application.ToLower() != "voicemail")
                return;

            foreach (Channel tempChannel in _channels)
                if (tempChannel.Uniqueid == uniqueid)
                {
                    channel = tempChannel;
                    break;
                }

            if (channel == null)
                return;

            channel.Exten = application;
            channel.StateDescription = "VoiceMailUp";
        }

        private void listen()
        {
            tcpServer.Start();

            while (true)
            {
                try
                {
                    Socket newConnection = tcpServer.AcceptSocket();
                    ManagedSocket managedSocket = new ManagedSocket(RandomFactory.Create().Next(1000000).ToString(), newConnection);
                    managedSocket.Error += new ManagedSocket.errorDelegate(SendErrorReport);
                    managedSocket.DataReceived += new ManagedSocket.dataReceivedDelegate(managedSocket_DataReceived);
                    managedSocket.SocketClosed += new ManagedSocket.socketClosedDelegate(managedSocket_SocketClosed);
                    managedSocket.ReadStart();
                    managedSocket.SendMessage("Response: Welcome\nCommand: N/A");

                    _clients.Add(managedSocket);
                }
                catch (InvalidOperationException e)
                {
                    SendErrorReport("", e);
                }
                catch (Exception e)
                {
                    SendErrorReport("AMI Proxy will no longer accept new connections", e);
                    break;
                }
            }

        }

        private void managedSocket_SocketClosed(ManagedSocket managedSocket)
        {
            if (_clients.Contains(managedSocket))
                _clients.Remove(managedSocket);

            if (_AMIProxyListeners.Contains(managedSocket))
                _AMIProxyListeners.Remove(managedSocket);

            if (_callStatListeners.Contains(managedSocket))
                _callStatListeners.Remove(managedSocket);

            managedSocket = null;
        }
        private void managedSocket_DataReceived(ManagedSocket managedSocket, Byte[] data)
        {
            String[] lines;

            command += Encoding.ASCII.GetString(data, 0, data.Length);

            lines = Regex.Split(command, "\\r\\n\\r\\n", RegexOptions.Multiline);

            for (Int32 i = 0; i < lines.Length - 1; i++)
            {
                ReadCommand(managedSocket, lines[i]);
            }

            command = lines[lines.Length - 1];
        }
        private void endListen(IAsyncResult result)
        {

        }
        private void ReadCommand(ManagedSocket managedSocket, String text)
        {
            if (Regex.Match(text, "(Command: Listen)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                ListenHandler(managedSocket, text);
            else if (Regex.Match(text, "(Command: AMIProxy)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                AMIProxyHandler(managedSocket);
            else if (Regex.Match(text, "(Command: CallStat)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                CallStatsHandler(managedSocket);
            else if (Regex.Match(text, "(Command: PhoneStateMonitor)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                PhoneStateMonitorHandler(managedSocket, text);
            else if (Regex.Match(text, "(Action:)", RegexOptions.Multiline & RegexOptions.IgnoreCase).Success)
                RelaytoAsterisk(managedSocket, text);
        }
        private void ListenHandler(ManagedSocket managedSocket, String text)
        {
            // Example Command

            // Command: Listen
            // Extension: 2000,2001,2002

            String extensionPattern = "(Extension: )([\\w\\d\\.,]*)";
            Match match;
            String[] extensions = { "" };

            match = Regex.Match(text, extensionPattern, RegexOptions.IgnoreCase);

            if (match.Success)
                extensions = Regex.Split(match.Result("$2"), ",");

            foreach (String extension in extensions)
            {
                if (extension == "")
                    continue;

                foreach (Phone phone in _phones)
                {
                    if (phone.Extension.ToLower() == extension)
                    {
                        if (!phone.Listeners.Contains(managedSocket))
                        {
                            phone.Listeners.Add(managedSocket);
                            managedSocket.SendMessage("Response: Successful\r\nCommand: Listen\r\nExtension: " + extension);
                            managedSocket.SendMessage("Action: NewPhone\r\n" +
                                                        "Extension: " + phone.Extension + "\r\n" +
                                                        "IP: " + phone.IP + "\r\n" +
                                                        "Port: " + phone.Port + "\r\n" +
                                                        "Status: " + phone.CurrentStatus);

                            managedSocket.SendMessage(phone.getCurrentChannels());
                        }

                        break;
                    }
                }
            }

        }
        private void AMIProxyHandler(ManagedSocket managedSocket)
        {
            //EXAMPLE MESSAGES
            //Command: AMIProxy

            //String userPattern = "(User: )([\\w\\d\\.,]*)";
            //String secretPattern = "(Secret: )([\\w\\d\\.,]*)";

            _AMIProxyListeners.Add(managedSocket);
            managedSocket.SendMessage("Response: Successful\r\nCommand: AMIProxy");
        }
        private void CallStatsHandler(ManagedSocket managedSocket)
        {
            //EXAMPLE MESSAGES
            //Command: AMICallStat

            _callStatListeners.Add(managedSocket);
            managedSocket.SendMessage("Response: Successful\r\nCommand: AMICallStat");
        }
        private void PhoneStateMonitorHandler(ManagedSocket managedSocket, String text)
        {
            // Example Command

            // Command: PhoneStateMonitor
            // Extension: 2000,2001,2002

            String extensionPattern = "(Extension: )([\\w\\d\\.,]*)";
            Match match;
            String[] extensions = { "" };

            match = Regex.Match(text, extensionPattern, RegexOptions.IgnoreCase);

            if (match.Success)
                extensions = Regex.Split(match.Result("$2"), ",");

            if (extensions.Length > 0 && extensions[0] == "all")
                if (_phones.Count > 0)
                    extensions = (from e in _phones
                                  select e.Extension).ToArray();

            foreach (String extension in extensions)
            {
                if (extension == "")
                    continue;

                foreach (Phone phone in _phones)
                {
                    if (phone.Extension.ToLower() == extension)
                    {
                        if (!phone.MonitorListeners.Contains(managedSocket))
                        {
                            phone.MonitorListeners.Add(managedSocket);
                            managedSocket.SendMessage("Response: Successful\r\nCommand: PhoneStateMonitor\r\nExtension: " + extension + "\r\n");

                            if (phone.IP != "-none-")
                                managedSocket.SendMessage("PhoneStateNew,Extension: " + phone.Extension + ",State: " + phone.PhoneState.ToString() + "\r\n");
                        }

                        break;
                    }
                }
            }
        }
        private void RelaytoAsterisk(ManagedSocket managedSocket, String text)
        {

            if (!text.Contains("ActionID:"))
                text += "\nActionID: #" + RandomFactory.Create().Next(100000).ToString() + "#_" + managedSocket.Key;
            else
            {
                Int32 ActionIDIndex = -1;
                Int32 NextNewLine = -1;
                text = Regex.Replace(text, "\r\n", "\n");
                ActionIDIndex = text.IndexOf("ActionID:");
                NextNewLine = text.IndexOf("\n", ActionIDIndex);

                if (NextNewLine == -1)
                    text += "_" + managedSocket.Key;
                else
                    text = text.Insert(NextNewLine, "_" + managedSocket.Key);
            }

            SendMessageToAsterisk(text);
        }
        private void SendMessageToSockets(List<ManagedSocket> sockets, String message)
        {
            Byte[] data = Encoding.ASCII.GetBytes(message + "\r\n\r\n");

            foreach (ManagedSocket socket in sockets)
            {
                socket.SendData(data);
            }
        }
        private Boolean CheckActionIDForProxy(String message)
        {
            Int32 ActionIDIndex = -1;
            Int32 NextNewLine = -1;
            String ActionIDProxyKey = "";
            String[] ActionIDProxyKeyParts;
            String newMessage = "";
            message = Regex.Replace(message, "\r\n", "\n");
            ActionIDIndex = message.IndexOf("ActionID:");
            NextNewLine = message.IndexOf("\n", ActionIDIndex);

            if (NextNewLine != -1)
                ActionIDProxyKey = message.Substring(ActionIDIndex + 10, NextNewLine - (ActionIDIndex + 10));
            else
                ActionIDProxyKey = message.Substring(ActionIDIndex + 10, message.Length - (ActionIDIndex + 10));

            if (ActionIDProxyKey.Contains("_"))
            {
                ActionIDProxyKeyParts = ActionIDProxyKey.Split('_');
                ActionIDProxyKey = ActionIDProxyKeyParts[ActionIDProxyKeyParts.Length - 1];

                foreach (ManagedSocket managedSocket in _AMIProxyListeners)
                    if (ActionIDProxyKey == managedSocket.Key)
                    {
                        newMessage = Regex.Replace(message, ("_" + managedSocket.Key), "");
                        managedSocket.SendMessage(newMessage);
                        return true;
                    }

            }

            return false;

        }
        private void SendMessageToAsterisk(String message)
        {
            if (!message.Contains("ActionID:"))
                message += "\nActionID: #" + RandomFactory.Create().Next(100000).ToString() + "#";

            try
            {
                Byte[] data = Encoding.ASCII.GetBytes(message + "\n\n");
                netStream.Write(data, 0, data.Length);
            }
            catch (ArgumentNullException e)
            {
                SendErrorReport("", e);
            }
            catch (ArgumentOutOfRangeException e)
            {
                SendErrorReport("", e);
            }
            catch (IOException e)
            {
                SendErrorReport("", e);
            }
            catch (ObjectDisposedException e)
            {
                SendErrorReport("", e);
            }
            catch (Exception e)
            {
                SendErrorReport("", e);
            }
        }
        private void LogMessage(String extraMessage)
        {
            if (!_logging)
                return;

            if (_todaysDate == DateTime.MinValue || _todaysDate.ToString("yyyy/MM/dd") != DateTime.Now.ToString("yyyy/MM/dd"))
            {
                String _path = _logPath;

                if (!Directory.Exists(_path))
                    Directory.CreateDirectory(_path);

                try
                {
                    if (_writer != null)
                    {
                        _writer.Flush();
                        _writer.Close();
                    }

                    _writer = new StreamWriter(_path + DateTime.Now.ToString("yyyy_MM_dd") + ".txt", true);
                    _writer.WriteLine(Environment.NewLine + Environment.NewLine + "################# Application Start " + DateTime.Now.ToString("HH:mm") + " #################" + Environment.NewLine + Environment.NewLine);
                    _writer.Flush();
                }
                catch (Exception e) { }

                _todaysDate = DateTime.Now;
            }

            if (_writer != null)
            {
                try
                {
                    _writer.WriteLine(extraMessage + "\r\n\r\n");
                    _writer.Flush();
                }
                catch (Exception e)
                {
                    SendErrorReport("", e);
                }
            }            
        }
        private String GenerateErrorReportBody(String body, Exception e)
        {
            String _body;
            IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName());
            IPAddress[] ipaddr = ipEntry.AddressList;
            Int32 counter = 0;

            _body = "Error\n" +
                   "==============\n" +
                   "Computer    : " + Environment.MachineName + "\n";

            _body += "User        :- AMI Proxy\n";

            for (Int32 i = 0; i < ipaddr.Length; i++)
            {
                if (counter == 0)
                    _body += "IP's :- \n" + ipaddr[i].ToString() + "\n";
                else
                    _body += "          : " + ipaddr[i].ToString() + "\n";

                counter++;
            }

            if (body != "")
            {
                _body += "\n\n\n" +
                        "Information\n" +
                        "=============\n" +
                        body;
            }

            if (e != null)
            {
                _body += "\n\n\n" +
                        "Source\n" +
                        "=============\n" +
                        e.Source + "\n\n\n" +
                        "Method\n" +
                        "=============\n" +
                        e.TargetSite + "\n\n\n" +
                        "Message\n" +
                        "=============\n" +
                        e.Message + "\n\n\n" +
                        "Stack Trace\n" +
                        "=============\n" +
                        e.StackTrace + "\n\n\n";

                Exception innerEx = e.InnerException;

                while (innerEx != null)
                {
                    _body += "\n\n\n" +
                        "Source\n" +
                        "=============\n" +
                        innerEx.Source + "\n\n\n" +
                        "Method\n" +
                        "=============\n" +
                        innerEx.TargetSite + "\n\n\n" +
                        "Message\n" +
                        "=============\n" +
                        innerEx.Message + "\n\n\n" +
                        "Stack Trace\n" +
                        "=============\n" +
                        innerEx.StackTrace + "\n\n\n";

                    innerEx = innerEx.InnerException;
                }
            }

            return _body;

        }
        private void SendEmailToSMTP(MailMessage msg)
        {
            SmtpClient smtp = new SmtpClient();

            try
            {
                smtp.Host = _smtpServer;
                smtp.Send(msg);
            }
            catch (Exception)
            { }
        }

        // Classes
        public class Phone
        {
            // Private variables
            private String _extension;
            private String _protocol;
            private String _IP;
            private Int32 _port;
            private String _currentStatus;
            private ChannelsCollection _channels;
            private List<ManagedSocket> _listeners = new List<ManagedSocket>();
            private List<ManagedSocket> _monitorListeners = new List<ManagedSocket>();
            private Dictionary<Double, Channel> _clonedChannels = new Dictionary<Double, Channel>();
            private PhoneUpdatedDelegate _phoneUpdated;
            private ChannelUpdatedDelegate _channelUpdated;
            private CallStatGeneratedDelegate _callStatGenerated;
            private ChannelRemovedDelegate _channelRemoved;
            private List<CallStat> _callStats = new List<CallStat>();
            private Hashtable _activeChannels = new Hashtable();
            private Hashtable _ringingChannels = new Hashtable();
            private State _phoneState;

            // Public Delegates
            public delegate void PhoneUpdatedDelegate(Phone phone);
            public delegate void ChannelUpdatedDelegate(Channel channel);
            public delegate void CallStatGeneratedDelegate(CallStat callstat);
            public delegate void ChannelRemovedDelegate(Channel channel);

            // Public Events
            public event PhoneUpdatedDelegate PhoneUpdated
            {
                add { _phoneUpdated += value; }
                remove { _phoneUpdated -= value; }
            }
            public event ChannelUpdatedDelegate ChannelUpdated
            {
                add { _channelUpdated += value; }
                remove { _channelUpdated -= value; }
            }
            public event CallStatGeneratedDelegate CallStatGenerated
            {
                add { _callStatGenerated += value; }
                remove { _callStatGenerated -= value; }
            }
            public event ChannelRemovedDelegate ChannelRemoved
            {
                add { _channelRemoved += value; }
                remove { _channelRemoved -= value; }
            }

            // Public Properties
            public String Extension
            {
                get { return _extension; }
                set
                {
                    _extension = value;
                    OnPhoneUpdated();
                }
            }
            public String Protocol
            {
                get { return _protocol; }
                set
                {
                    _protocol = value;
                    OnPhoneUpdated();
                }
            }
            public String IP
            {
                get { return _IP; }
                set
                {
                    _IP = value;
                    OnPhoneUpdated();
                }
            }
            public Int32 Port
            {
                get { return _port; }
                set
                {
                    _port = value;
                    OnPhoneUpdated();
                }
            }
            public String CurrentStatus
            {
                get { return _currentStatus; }
                set
                {
                    _currentStatus = value;
                    OnPhoneUpdated();
                }
            }
            public List<ManagedSocket> Listeners
            {
                get { return _listeners; }
            }
            public List<ManagedSocket> MonitorListeners
            {
                get { return _monitorListeners; }
            }
            public Dictionary<Double, Channel> ClonedChannels
            {
                get { return _clonedChannels; }
            }
            public ChannelsCollection Channels
            {
                get { return _channels; }
            }
            public State PhoneState
            {
                get { return _phoneState; }
            }

            // Public Constructors
            public Phone(String extension)
            {
                _channels = new ChannelsCollection(this);
                _extension = extension;
            }

            // Internal methods
            internal void OnPhoneUpdated()
            {
                if (_phoneUpdated != null)
                    _phoneUpdated.RaiseEventSafely(new Object[] { this });

                String phoneUpate = "Action: PhoneUpdated\r\n" +
                                                       "Extension: " + Extension + "\r\n" +
                                                       "IP: " + IP + "\r\n" +
                                                       "Port: " + Port + "\r\n" +
                                                       "Status: " + CurrentStatus;

                sendMessageToListeners(phoneUpate);

            }
            internal void OnPhoneStateUpdated()
            {
                sendMessageToMonitorListeners("PhoneStateUpdate,Extension: " + Extension + ",State: " + _phoneState.ToString() + "\r\n");
            }

            internal void OnChannelUpdated(Channel channel)
            {
                String channelMessage;

                processCallStat(channel);

                if (!channel.StateDescription.StartsWith("Hung up"))
                    _currentStatus = channel.StateDescription;
                else
                    _currentStatus = "";

                OnPhoneUpdated();

                if (_channelUpdated != null)
                    _channelUpdated.RaiseEventSafely(new Object[] { channel });

                channelMessage = "Action: ChannelMessage\r\n" +
                                 "Type: Update\r\n" +
                                 "Extension: " + channel.CurrentPhone.Extension + "\r\n" +
                                 "Dialed: " + channel.Exten + "\r\n" +
                                 "ChannelID: " + channel.Uniqueid + "\r\n" +
                                 "StatusDesc: " + channel.StateDescription + "\r\n" +
                                 "OnHold: " + channel.OnHold.ToString() + "\r\n" +
                                 "OnHoldPlaced: " + channel.PlacedOnHold.ToString() + "\r\n";

                if (channel.IncomingCall != null)
                {
                    channelMessage += "CalledFrom: " + channel.IncomingCall.CallerIDNum + "\r\n";
                    channelMessage += "CalledFromName: " + channel.IncomingCall.CallerIDName;
                }
                else
                {
                    channelMessage += "CalledFrom: \r\n";
                    channelMessage += "CalledFromName: ";
                }

                sendMessageToListeners(channelMessage);

                if (!channel.Alive)
                {
                    RemoveChannelFromLists(channel.Uniqueid);

                    _channels.Remove(channel);
                    channel = null;
                }

            }
            internal void RemoveChannelFromLists(Double id)
            {
                if (_activeChannels.Contains(id))
                {
                    _activeChannels.Remove(id);
                }

                if (_ringingChannels.Contains(id))
                {
                    _ringingChannels.Remove(id);
                }
            }
            internal void OnChannelUniqueidChanged(Double oldID, Double newID)
            {
                foreach (CallStat callstat in _callStats)
                    if (callstat.ID == oldID)
                    {
                        callstat.ID = newID;
                        break;
                    }
            }
            internal void Channel(Channel channel)
            {

                CallStat removeMe = null;
                foreach (CallStat callstat in _callStats)
                    if (callstat.ID == channel.Uniqueid)
                    {
                        removeMe = callstat;
                        break;
                    }

                if (removeMe != null)
                    _callStats.Remove(removeMe);

                String channelMessage = "Action: ChannelMessage\r\n" +
                                        "Type: Removed\r\n" +
                                        "Extension: " + channel.CurrentPhone.Extension + "\r\n" +
                                        "ChannelID: " + channel.Uniqueid + "\r\n" +
                                        "StatusDesc: " + channel.StateDescription + "";

                sendMessageToListeners(channelMessage);
            }
            
            internal void OnCallStatGenerated(CallStat callstat)
            {
                if (_callStatGenerated != null)
                    _callStatGenerated(callstat);
            }
            internal void OnChannelStateUpdated(Channel channel, State channelState)
            {
                State currentPhoneState = PhoneState;

                if (channelState == State.Busy && _phoneState != State.Busy)
                {
                    _activeChannels.Add(channel.Uniqueid, channel);
                    _phoneState = channelState;
                }
                else if (channelState == State.Busy && _phoneState == State.Busy)
                {
                    _activeChannels.Add(channel.Uniqueid, channel);
                }
                else if (channelState == State.Recieving && _phoneState == State.Free)
                {
                    _ringingChannels.Add(channel.Uniqueid, channel);

                    if (_activeChannels.Count == 0)
                        _phoneState = channelState;
                }
                else if (channelState == State.Recieving)
                {
                    _ringingChannels.Add(channel.Uniqueid, channel);
                }
                else if (channelState == State.Free)
                {
                    if (_activeChannels.ContainsKey(channel.Uniqueid))
                    {
                        RemoveChannelFromLists(channel.Uniqueid);

                        if (_ringingChannels.Count > 0 && _activeChannels.Count == 0)
                            _phoneState = State.Recieving;
                        else if (_ringingChannels.Count == 0 && _activeChannels.Count == 0)
                            _phoneState = State.Free;
                    }
                    else if (_ringingChannels.ContainsKey(channel.Uniqueid))
                    {
                        RemoveChannelFromLists(channel.Uniqueid);

                        if (_ringingChannels.Count == 0 && _activeChannels.Count == 0)
                            _phoneState = State.Free;
                    }
                }

                if (currentPhoneState != PhoneState)
                    OnPhoneStateUpdated();
            }
            internal void ChannelAdded(Channel channel)
            {
                CallStat callstat = new CallStat(channel.Uniqueid, _extension);
                callstat.StartDateTimeUTC = DateTime.UtcNow;
                _callStats.Add(callstat);
                sendMessageToListeners(CreateChannelMessage(channel));
            }
            internal String CreateChannelMessage(Channel channel)
            {
                Double targetID = -1;

                foreach (KeyValuePair<Double, Channel> item in _clonedChannels)
                    if (item.Key == channel.Uniqueid)
                    {
                        targetID = item.Value.Uniqueid;
                        break;
                    }

                String channelMessage = "Action: ChannelMessage\r\n";

                if (targetID == -1D)
                    channelMessage += "Type: Added\r\n";
                else
                {
                    if (_clonedChannels.ContainsKey(channel.Uniqueid))
                        _clonedChannels.Remove(channel.Uniqueid);

                    channelMessage += "Type: Cloned\r\n" +
                                      "ChannelIDTarget: " + targetID.ToString() + "\r\n";

                    if (channel.OutgoingCall != null)
                    {
                        channelMessage += "CalledTo: " + channel.OutgoingCall.CallerIDNum + "\r\n";
                        channelMessage += "CalledToName: " + channel.OutgoingCall.CallerIDName + "\r\n";
                    }
                    else
                    {
                        channelMessage += "CalledTo: \r\n";
                        channelMessage += "CalledToName: \r\n";
                    }
                }

                channelMessage += "Extension: " + channel.CurrentPhone.Extension + "\r\n" +
                                  "ChannelID: " + channel.Uniqueid + "\r\n" +
                                  "StatusDesc: " + channel.StateDescription + "\r\n";

                if (channel.IncomingCall != null)
                {
                    channelMessage += "CalledFrom: " + channel.IncomingCall.CallerIDNum + "\r\n";
                    channelMessage += "CalledFromName: " + channel.IncomingCall.CallerIDName;
                }
                else
                {
                    channelMessage += "CalledFrom: \r\n";
                    channelMessage += "CalledFromName: ";
                }

                return channelMessage;
            }

            internal void OnChannelRemoved(Channel channel)
            {

                CallStat removeMe = null;
                foreach(CallStat callstat in _callStats)
                    if (callstat.ID == channel.Uniqueid)
                    {
                        removeMe = callstat;
                        break;
                    }

                if (removeMe != null)
                    _callStats.Remove(removeMe);

                String channelMessage = "Action: ChannelMessage\r\n" +
                                        "Type: Removed\r\n" +
                                        "Extension: " + channel.CurrentPhone.Extension + "\r\n" +
                                        "ChannelID: " + channel.Uniqueid + "\r\n" +
                                        "StatusDesc: " + channel.StateDescription;

                sendMessageToListeners(channelMessage);

                RemoveChannelFromLists(channel.Uniqueid);

                if (_channelRemoved != null)
                    _channelRemoved.RaiseEventSafely(new Object[] { channel });
            }
            internal void sendMessageToListeners(String message)
            {
                Int32 counter = 0;
                Stack removeItems = new Stack();

                Byte[] data = Encoding.ASCII.GetBytes(message + "\r\n\r\n");

                foreach (ManagedSocket managedSocket in _listeners.ToArray())
                {
                    if (managedSocket.Active)
                    {
                        managedSocket.SendData(data);
                    }
                    else
                        removeItems.Push(counter);

                    counter++;
                }

                lock (_listeners)
                {
                    foreach (Int32 index in removeItems)
                        _listeners.RemoveAt(index);
                }

            }
            internal void sendMessageToMonitorListeners(String message)
            {
                Int32 counter = 0;
                Stack removeItems = new Stack();

                Byte[] data = Encoding.ASCII.GetBytes(message + "\r\n\r\n");

                foreach (ManagedSocket managedSocket in _monitorListeners.ToArray())
                {
                    if (managedSocket.Active)
                    {
                        managedSocket.SendData(data);
                    }
                    else
                        removeItems.Push(counter);

                    counter++;
                }

                lock (_monitorListeners)
                {
                    foreach (Int32 index in removeItems)
                        _monitorListeners.RemoveAt(index);
                }

            }
            internal void processCallStat(Channel channel)
            {
                CallStat callStat = null;

                foreach (CallStat callstat in _callStats)
                    if (callstat.ID == channel.Uniqueid)
                    {
                        callStat = callstat;
                        break;
                    }

                if (callStat == null)
                    return;

                //if (channel.StateDescription == "Up" && callStat.AnsweredDateTime == DateTime.MinValue)
                //    callStat.AnsweredDateTime = DateTime.UtcNow;

                if (channel.IncomingCall != null )
                {
                    callStat.Direction = CallStat.Direction_.incoming;
                    callStat.Number = channel.IncomingCall.CallerIDNum;
                }
                else if (channel.OutgoingCall != null)
                {
                    callStat.Direction = CallStat.Direction_.outgoing;
                    callStat.Number = channel.Exten;
                }

                if (channel.IncomingCall != null && channel.IncomingCall.JoinedQueue)
                {
                    callStat.Direction = CallStat.Direction_.queue;
                    callStat.Number = channel.IncomingCall.QueueExtension;
                }

                if (channel.BridgedTo != null)
                {
                    callStat.AnsweredDateTimeUTC = channel.BridgedTo.BridgedDateTime;

                    Channel.ChannelBridge testBridge = channel.BridgedTo;
                    callStat.Internal = (
                                        (testBridge.FromChannel.Exten == "" || (testBridge.FromChannel.Exten.Length >= 1 && testBridge.FromChannel.Exten.Length <= 4 && testBridge.FromChannel.Exten != "999"))
                                        && (testBridge.FromChannel.CallerIDNum == "" || (testBridge.FromChannel.CallerIDNum.Length >= 1 && testBridge.FromChannel.CallerIDNum.Length <= 4 && testBridge.FromChannel.CallerIDNum != "999"))
                                        &&
                                        (testBridge.ToChannel.Exten == "" || (testBridge.ToChannel.Exten.Length >= 1 && testBridge.ToChannel.Exten.Length <= 4 && testBridge.ToChannel.Exten != "999"))
                                        && (testBridge.ToChannel.CallerIDNum == "" || (testBridge.ToChannel.CallerIDNum.Length >= 1 && testBridge.ToChannel.CallerIDNum.Length <= 4 && testBridge.ToChannel.CallerIDNum != "999"))
                                        );
                }
                else if (callStat.Number != null)
                    callStat.Internal = (callStat.Number.Length >= 1 && callStat.Number.Length <= 4 && callStat.Number != "999");

                if (channel.StateDescription.StartsWith("Hung up"))
                    callStat.EndDateTimeUTC = DateTime.UtcNow;

                if (channel.Transfered != null)
                {
                    callStat.TransferedFromExtension = channel.Transfered.TransferedFrom.CurrentPhone.Extension;
                    callStat.TransferType = channel.Transfered.TransferType;
                }
                else if (channel.Transfered == null)
                {
                    callStat.TransferedFromExtension = "";
                    callStat.TransferType = "";
                }

                if (channel.LeftQueue)
                {
                    callStat.TransferedFromExtension = channel.QueueExtension;
                    callStat.TransferType = "Queue";
                }

                if (channel.Exten.ToLower() == "voicemailmain" && callStat.Direction == CallStat.Direction_.notSet)
                    callStat.Direction = CallStat.Direction_.voiceMail;

                if (!channel.Alive)
                {
                    OnCallStatGenerated(callStat);
                    _callStats.Remove(callStat);
                    callStat = null;
                }

            }

            // Public Methods
            public String getCurrentChannels()
            {
                String message = "";

                foreach (Channel channel in _channels)
                {
                    if (channel.Alive)
                        message += CreateChannelMessage(channel) + "\r\n\r\n";
                }

                if (message == "")
                    return "";

                return message.Substring(0, message.Length - 4);
            }

            // Public Class
            public class ChannelsCollection : CollectionBase
            {
                //Private Variables
                private Phone _owner;

                public Channel this[int Index]
                {
                    get { return (Channel)this.List[Index]; }
                    set { this.List[Index] = value; }
                }

                //Constructors
                /// <summary>Creates an instance of ListViewSubItemOBiCollection.</summary>
                public ChannelsCollection(Phone Owner)
                {
                    this._owner = Owner;
                }

                //Methods
                public void AddRange(Channel[] items)
                {
                    foreach (Channel item in items)
                    {
                        Add(item);
                    }
                }
                public Int32 IndexOf(Channel item)
                {
                    return this.List.IndexOf(item);
                }
                public void Add(Channel item)
                {
                    this.List.Add(item);
                    item.LinkPhone(_owner);
                    _owner.ChannelAdded(item);
                    item.ChannelUpdated += new Channel.ChannelUpdatedDelegate(_owner.OnChannelUpdated);
                    item.UniqueidChanged += new Channel.UniqueidChangedDelegate(_owner.OnChannelUniqueidChanged);
                    item.ChannelStateUpdated += new Channel.ChannelStateUpdatedDelegate(_owner.OnChannelStateUpdated);
                }
                public void Remove(Channel item)
                {
                    if (this.List.Contains(item))
                    {
                        this.List.Remove(item);
                        item.UnlinkPhone();
                        _owner.OnChannelRemoved(item);
                        item.ChannelUpdated -= _owner.OnChannelUpdated;
                        item.UniqueidChanged -= new Channel.UniqueidChangedDelegate(_owner.OnChannelUniqueidChanged);
                    }
                }
            }
            public class CallStat
            {

                // Private Variables
                private Double _id = -1;
                private String _extension = "";
                private DateTime _startDateTimeUTC = DateTime.Parse("1990/01/01 00:00");
                private DateTime _endDateTimeUTC = DateTime.Parse("1990/01/01 00:00");
                private DateTime _answeredDateTimeUTC = DateTime.Parse("1990/01/01 00:00");
                private Direction_ _direction;
                private Boolean _internal;
                private String _number;
                private String _transferedFromExtension;
                private String _transferType;

                // Public Properties
                public Double ID
                { 
                    get { return _id; }
                    set { _id = value; }
                }
                public DateTime StartDateTimeUTC
                {
                    get { return _startDateTimeUTC; }
                    set { _startDateTimeUTC = value;}
                }
                public DateTime AnsweredDateTimeUTC
                {
                    get { return _answeredDateTimeUTC; }
                    set { _answeredDateTimeUTC = value; }
                }
                public DateTime EndDateTimeUTC
                {
                    get { return _endDateTimeUTC; }
                    set { _endDateTimeUTC = value; }
                }
                public Direction_ Direction
                {
                    get { return _direction; }
                    set { _direction = value; }
                }
                public Boolean Internal
                {
                    get { return _internal; }
                    set { _internal = value; }
                }
                public String Number
                {
                    get { return _number; }
                    set { _number = value; }
                }
                public String Extension
                {
                    get { return _extension; }
                }
                public String TransferedFromExtension
                {
                    get { return _transferedFromExtension; }
                    set { _transferedFromExtension = value; }
                }
                public String TransferType
                {
                    get { return _transferType; }
                    set { _transferType = value; }
                }

                // Public Enums
                public enum Direction_
                {
                    notSet,
                    incoming,
                    outgoing,
                    queue,
                    voiceMail
                }

                // Public Constructors
                public CallStat(Double id, String extension)
                {
                    _id = id;
                    _extension = extension;
                }

            }

        }
        public class Channel
        {
            // Private Variables
            private String _name = "";
            private Boolean _alive = true;
            private Double _uniqueid;
            private Channel _outgoingCall;
            private Channel _incomingCall;
            private String _callerIDNum = "";
            private String _callerIDName = "";
            private Phone _phone;
            private List<Object> _cronilogicalHistory = new List<Object>();
            private ChannelUpdatedDelegate _channelUpdated;
            private UniqueidChangedDelegate _uniqueidChanged;
            private String _exten = "";
            private Boolean _onHold = false;
            private Boolean _placedOnHold = false;
            private String _stateDescription = "";
            private ChannelTransfer _transfered = null;
            private ChannelBridge _bridgedTo = null;
            private Boolean _internalCall = false;
            private Boolean _cloned = false;
            private Boolean _splitDial = false;
            private Boolean _leftQueue = false;
            private Boolean _joinedQueue = false;
            private String _queueExtension = "";
            private State _state = State.Free;
            private ChannelStateUpdatedDelegate _channelStateUpdated;
            private Boolean _redirected = false;
            private Phone _redirectedFrom = null;

            // Public Delegates
            public delegate void ChannelUpdatedDelegate(Channel channel);
            public delegate void UniqueidChangedDelegate(Double oldID, Double newID);
            public delegate void ChannelStateUpdatedDelegate(Channel channel, State phoneState);

            // Public Events
            public event ChannelUpdatedDelegate ChannelUpdated
            {
                add { _channelUpdated += value; }
                remove { _channelUpdated -= value; }
            }
            public event UniqueidChangedDelegate UniqueidChanged
            {
                add { _uniqueidChanged += value; }
                remove { _uniqueidChanged -= value; }
            }
            public event ChannelStateUpdatedDelegate ChannelStateUpdated
            {
                add { _channelStateUpdated += value; }
                remove { _channelStateUpdated -= value; }
            }

            // Public Constructors
            public Channel(String name)
            {
                _name = name;
            }

            // Public Properties
            public String Name
            {
                get { return _name; }
                set
                {
                    if (_name == value)
                        return;

                    _name = value;
                    OnChannelUpdated();
                }
            }
            public String StateDescription
            {
                get { return _stateDescription;}
                set
                {
                    if (_stateDescription == value)
                        return;

                    _stateDescription = value;
                    OnChannelUpdated();
                    CheckState();
                }
            }
            public Boolean Alive
            {
                get { return _alive; }
                set
                {
                    if (_alive == value)
                        return;

                    _alive = value;
                    OnChannelUpdated();
                }
            }
            public Double Uniqueid
            {
                get { return _uniqueid; }
                set
                {
                    if (_uniqueid == value)
                        return;

                    Double oldID = _uniqueid;
                    _uniqueid = value;
                    OnUniqueidChanged(oldID, _uniqueid);
                    OnChannelUpdated();
                }
            }
            public Channel OutgoingCall
            {
                get { return _outgoingCall; }
                set
                {
                    if (_outgoingCall == value)
                        return;

                    _outgoingCall = value;
                    OnChannelUpdated();
                    CheckState();
                }
            }
            public Channel IncomingCall
            {
                get { return _incomingCall; }
                set
                {
                    if (_incomingCall == value)
                        return;

                    _incomingCall = value;
                    OnChannelUpdated();
                }
            }
            public String CallerIDNum
            {
                get { return _callerIDNum; }
                set
                {
                    if (_callerIDNum == value)
                        return;

                    _callerIDNum = value;
                    OnChannelUpdated();
                }
            }
            public String CallerIDName
            {
                get { return _callerIDName; }
                set
                {
                    if (_callerIDName == value)
                        return;

                    _callerIDName = value;
                    OnChannelUpdated();
                }
            }
            public String Exten
            {
                get { return _exten; }
                set
                {
                    if (_exten == value)
                        return;

                    _exten = value;
                    OnChannelUpdated();
                }
            }
            public Boolean OnHold
            {
                get { return _onHold; }
                set
                {
                    if (_onHold == value)
                        return;

                    _onHold = value;
                    OnChannelUpdated();
                }

            }
            public Boolean PlacedOnHold
            {
                get { return _placedOnHold; }
                set
                {
                    if (_placedOnHold == value)
                        return;

                    _placedOnHold = value;
                    OnChannelUpdated();
                }
            }
            public Phone CurrentPhone
            {
                get { return _phone; }
            }
            public ChannelTransfer Transfered
            {
                get { return _transfered; }
                set
                {
                    if (_transfered == value)
                        return;

                    _transfered = value;
                    OnChannelUpdated(); 
                }
            }
            public ChannelBridge BridgedTo
            {
                get { return _bridgedTo; }
                set
                {
                    if (_bridgedTo == value)
                        return;

                    _bridgedTo = value;
                    OnChannelUpdated();
                }
            }
            public Boolean InternalCall
            {
                get { return _internalCall; }
                set { _internalCall = value; }
            }
            public Boolean Cloned
            {
                get { return _cloned; }
                set { _cloned = value; }
            }
            public Boolean SplitDial
            {
                get { return _splitDial; }
                set { _splitDial = value; }
            }
            public Boolean LeftQueue
            {
                get { return _leftQueue; }
                set { _leftQueue = value; }
            }
            public Boolean JoinedQueue
            {
                get { return _joinedQueue; }
                set 
                {
                    if (_joinedQueue == value)
                        return;

                    _joinedQueue = value;
                    CheckState();
                }
            }
            public String QueueExtension
            {
                get { return _queueExtension; }
                set { _queueExtension = value; }
            }
            public State State
            {
                get { return _state; }
                internal set { _state = value; }
            }
            public Boolean Redirected
            {
                get { return _redirected; }
                set { _redirected = value; }
            }
            public Phone RedirectedFrom
            {
                get { return _redirectedFrom; }
                set { _redirectedFrom = value; }
            }

            // Internal Methods
            internal void OnChannelUpdated()
            {
                if (_channelUpdated != null)
                    _channelUpdated.RaiseEventSafely(new Object[] { this });
            }
            internal void OnUniqueidChanged(Double oldID, Double newID)
            {
                if (_uniqueidChanged != null)
                    _uniqueidChanged.RaiseEventSafely(new Object[] { oldID, newID });
            }
            internal void OnChannelStateUpdated(Channel channel, State phoneState)
            {
                if (_channelStateUpdated != null)
                    _channelStateUpdated.RaiseEventSafely(new Object[] { channel, phoneState });
            }

            // Public Methods
            public void LinkPhone(Phone phone)
            {
                _phone = phone;
            }
            public void UnlinkPhone()
            {
                //_phone = null;
            }

            // Private Methods

            private void CheckState()
            {
                Boolean makeChange = false;

                if (StateDescription == "Ringing" || StateDescription == "Ring" || StateDescription.Contains("Calling"))
                    if (OutgoingCall != null || JoinedQueue)
                    {
                        if (_state != AMIProxy.State.Busy)
                        {
                            _state = State.Busy;
                            makeChange = true;
                        }
                    }
                    else
                    {
                        if (_state == AMIProxy.State.Free)
                        {
                            _state = State.Recieving;
                            makeChange = true;
                        }
                    }

                if (StateDescription == "Up" || StateDescription == "VoiceMailUp")
                    if (_state != AMIProxy.State.Busy)
                    {
                        _state = State.Busy;
                        makeChange = true;
                    }

                if (StateDescription.ToLower().Contains("hung up") || StateDescription.Contains("Caller canceled"))
                    if (_state != AMIProxy.State.Free)
                    {
                        _state = State.Free;
                        makeChange = true;
                    }

                if (makeChange)
                    OnChannelStateUpdated(this, _state);
            }

            // Public Classes
            public class ChannelTransfer
            { 
                
                // Private Variables
                private Channel _transferedFrom;
                private String _transferType;
                private Boolean _onHold;

                // Public Priperties
                public Channel TransferedFrom
                {
                    get{ return _transferedFrom;}
                }
                public String TransferType
                {
                    get { return _transferType; }
                }
                public Boolean OnHold
                {
                    get { return _onHold; }
                }

                // Public Constructors
                public ChannelTransfer(Channel transferedFrom, String transferType, Boolean onHold)
                {
                    _transferedFrom = transferedFrom;
                    _transferType = transferType;
                    _onHold = onHold;
                }

            }
            public class ChannelBridge
            {
                // Private Variables
                private Channel _fromChannel;
                private Channel _toChannel;
                private BridgeDirection _direction;
                private Boolean _linked;
                private DateTime _bridgedDateTime;

                // Public Enums
                public enum BridgeDirection
                {
                    Extended,
                    Accepted
                }

                // Public Properties
                public Channel FromChannel
                {
                    get { return _fromChannel; }
                    set { _fromChannel = value; }
                }
                public Channel ToChannel
                {
                    get { return _toChannel; }
                    set { _toChannel = value; }
                }
                public BridgeDirection Direction
                {
                    get { return _direction; }
                    set { _direction = value; }
                }
                public Boolean Linked
                {
                    get { return _linked; }
                    set { _linked = value; }
                }
                public DateTime BridgedDateTime
                {
                    get { return _bridgedDateTime; }
                }

                // Public Construcotrs
                public ChannelBridge(Channel fromChannel, Channel toChannel, BridgeDirection direction)
                {
                    _fromChannel = fromChannel;
                    _toChannel = toChannel;
                    _linked = true;
                    _direction = direction;
                    _bridgedDateTime = DateTime.UtcNow;
                }

            }
            public class ChannelEvent
            {

                // Private Variables
                private String _name;
                private Int32 _code;

                // Public Properties
                public String Name
                {
                    get { return _name; }
                    set { _name = value; }
                }
                public Int32 Code
                {
                    get { return _code; }
                    set { _code = value; }
                }

                // Public Construcotrs
                public ChannelEvent(String name, Int32 code)
                {
                    _name = name;
                    _code = code;
                }

            }

        }
        public static class Constants
        {
            public const String ENDLINEORNOTHING = @"(|$)";
            public const String ANYTHINGBUTENDLINE = "([ \\S ]*)";
            public const String PROTCOLEXTCHAN = ANYTHINGBUTENDLINE + "(/)([ \\.a-z0-9-]*)([\\S]*-)" + ANYTHINGBUTENDLINE;

            public const String ACTIONIDPATTERN = "(ActionID: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String ADDRESSPATTERN = "(Address: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String ADDRESSPATTERNWITHPORT = "(Address: )" + ANYTHINGBUTENDLINE + "(:)" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CALLERID1PATTERN = "(CallerID1: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CALLERID2PATTERN = "(CallerID2: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CALLERIDNAMEPATTERN = "(CallerIDName: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CALLERIDNUMPATTERN = "(CallerIDNum: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CAUSEPATTERN = "(Cause: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CAUSETXTPATTERN = "(Cause-txt: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CHANNELCONSOLEPATTERN = "(Channel: )" + ANYTHINGBUTENDLINE + "(/)" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CHANNELPATTERN = "(Channel: )" + PROTCOLEXTCHAN + ENDLINEORNOTHING;
            public const String CHANNELPATTERNLOCALEXTENSION = "(Newname: )" + ANYTHINGBUTENDLINE + "(/)" + ANYTHINGBUTENDLINE + "(-)" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CHANNELSTATEDESCPATTERN = "(ChannelStateDesc: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CHANNELSTATEPATTERN = "(ChannelState: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CHANNLELTYPEPATTERN = "(Channeltype: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String CLONECHANNELPATTERN = "(Clone: )" + ANYTHINGBUTENDLINE + "(/)" + ANYTHINGBUTENDLINE + "(-)" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String DESTUNIQUEIDPATTERN = "(DestUniqueID: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String DESTNAMEPATTERN = "(Destination: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String DESTNAMESIPBORDERPATTERN = "(Destination: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String DIALSTATUSPATTERN = "(DialStatus: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String DIALSTRINGPATTERN = "(Dialstring: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String EVENTNEWCHANNELPATTERN = "(Event: Newchannel)" + ENDLINEORNOTHING;
            public const String EXTENPATTERN = "(Exten: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String IPADDRESSPATTERN = "(IPaddress: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String IPPORTPATTERN = "(IPport: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String NEWNAMEPATTERN = "(Newname: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String OBJECTNAMEPATTERN = "(ObjectName: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String ORIGINALCHANNELPATTERN = "(Original: )" + ANYTHINGBUTENDLINE + "(/)" + ANYTHINGBUTENDLINE + "(-)" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String PEERPATTERN = "(Peer: )" + ANYTHINGBUTENDLINE + "(/)" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String PEERSTATUSPATTERN = "(PeerStatus: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String PORTPATTERN = "(Port: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String SIPCAUSEPATTERN = "(Variable: ~HASH~SIP_CAUSE~)" + PROTCOLEXTCHAN + "(~)" + ENDLINEORNOTHING;
            public const String VALUEPATTERN = "(Value: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String STATEPATTERN = "(State: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String SUBEVENTPATTERN = "(SubEvent: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String UNIQUEIDPATTERN = "(UniqueID: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String TARGETUNIQUEIDPATTERN = "(TargetUniqueid: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String UNIQUEID1PATTERN = "(Uniqueid1: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String UNIQUEID2PATTERN = "(Uniqueid2: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String TRANSFERTYPEPATTERN = "(TransferType: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String QUEUEPATTERN = "(Queue: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
            public const String APPLICATIONPATTERN = "(Application: )" + ANYTHINGBUTENDLINE + ENDLINEORNOTHING;
        }
    
    }
}
